//  文件不超过200秒 可以不做视频分割处理
package main

import (
	"bufio"
	"context"
	"fmt"
	"github.com/gin-gonic/gin"
	//	"log"
	"bytes"
	"encoding/json"
	"errors"
	ffprobe "github.com/vansante/go-ffprobe"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

type Storage struct {
	baseName         string
	baseNum          int
	baseMap          map[string]string
	baseChan         chan string
	dirPath          string
	serqueueName     string // 服务器发送队列
	cliqueueName     string //客户端发送队列
	mqUserName       string
	mqPassword       string
	mqIp             string
	mqPort           string
	baseFile         *os.File
	baseFileName     string
	baseFileDir      string // 下载的文件存储路径(分解后的文件)
	basePicDir       string // 生成文件的存储路径
	hostWanIP        string
	hostName         string
	baseShell        string
	downLoadDir      string //下载的原始文件的保存路径
	upLoadDir        string //  合成后的文件
	taskInputDir     string //task读文件的目录
	taskOutputDir    string // task合成后的数据
	downLoadFileName string //下载后文件名
	segmentDuration  int    //分割文件的大小
	fileWidth        int
	fileHeight       int
	fileDuration     string
	fileCodecName    string
	taskStatus       string
	datafile         string //数据文件记录水印的文字和ID对应关系
	decodePicDir     string
	encodePicDir     string
	decodeMarkFile   string
	decodeShell      string
	encodeShell      string
}

func InitStorage() *Storage {

	return &Storage{

		baseMap:  make(map[string]string),
		baseChan: make(chan string, 100000),
	}

}

// 初始化水印AI的对应数据
func (s *Storage) InitMarkId(filename string) {
	// 使用 ioutil.ReadFile 读取文件内容
	content, err := ioutil.ReadFile(filename)
	if err != nil {
		// 如果读取文件时出错，使用 log.Fatal 打印错误并退出程序
		log.Fatal(err)
	}

	// 打印文件内容
	//fmt.Printf("文件内容:\n%s", content)
	str1 := fmt.Sprintf("%s", content)
	// 开始数据整理
	str1 = strings.Replace(str1, "\\\"", "", -1)
	str1 = strings.Replace(str1, ": ", ":", -1)
	str1 = strings.Replace(str1, "\"{", "", -1)
	str1 = strings.Replace(str1, "}\"", "", -1)
	str1 = strings.Replace(str1, ", ", "|", -1)
	arrayLine := strings.Split(str1, "|")
	for i := 0; i < len(arrayLine); i++ {
		//     fmt.Println(arrayLine[i])
		arrayLine1 := strings.Split(arrayLine[i], ":")
		if len(arrayLine1) == 2 {
			s.baseMap[arrayLine1[0]] = arrayLine1[1]
		}
	}

	for k, v := range s.baseMap {
		fmt.Println(time.Now(), "水印ID和文字对应关系", "|"+k+"|"+v)
	}

}

// copyFileToDir 拷贝一个文件到指定的目录
func copyFileToDir(srcFile, destDir string) error {
	// 打开源文件
	src, err := os.Open(srcFile)
	if err != nil {
		return err
	}
	defer src.Close()

	// 获取文件名并创建目标文件路径
	fileName := filepath.Base(srcFile)
	destPath := filepath.Join(destDir, fileName)

	// 创建目标文件
	dest, err := os.Create(destPath)
	if err != nil {
		return err
	}
	defer dest.Close()

	// 拷贝文件内容
	_, err = io.Copy(dest, src)
	if err != nil {
		return err
	}

	return nil
}

// RunScript 执行一个名为 的脚本
func RunScript(strShell string) error {
	cmd := exec.Command("/bin/bash", strShell)

	// 运行命令，并获取其输出
	output, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("执行脚本时发生错误: %v, 输出: %s", err, output)
	}

	fmt.Println(time.Now(), "脚本输出:", string(output))
	return nil
}

// inputFile 是输入的视频文件名
// segmentDuration 是每个视频段的时长（秒）
// outputPath 是切割后的视频文件存储路径

// getVideoDuration 获取视频的总时长（秒）
func getVideoDuration(inputFile string) (int, error) {
	cmd := exec.Command("ffprobe", "-v", "error", "-show_entries", "format=duration",
		"-of", "default=noprint_wrappers=1:nokey=1", inputFile)

	out, err := cmd.StdoutPipe()
	if err != nil {
		return 0, err
	}

	if err := cmd.Start(); err != nil {
		return 0, err
	}

	scanner := bufio.NewScanner(out)
	scanner.Scan()
	durationStr := scanner.Text()

	duration, err := strconv.ParseFloat(strings.TrimSpace(durationStr), 64)
	if err != nil {
		return 0, err
	}

	if err := cmd.Wait(); err != nil {
		return 0, err
	}

	return int(duration), nil
}

// RemoveContents 删除指定目录下的所有文件和子目录
func (s *Storage) RemoveContents(dirPath string) error {
	// 打开指定的目录
	d, err := os.Open(dirPath)
	if err != nil {
		return err
	}
	defer d.Close()
	// 读取目录内容
	names, err := d.Readdirnames(-1)
	if err != nil {
		fmt.Println(time.Now(), err)
		return err
	}

	// 遍历目录内容并删除
	for _, name := range names {
		err = os.RemoveAll(filepath.Join(dirPath, name))
		if err != nil {
			return err
		}
	}

	return nil
}

func (s *Storage) RunTask() {
	var file, downLoadDir, WatermarkCon string
	for {

		data := <-s.baseChan
		time.Sleep(1e9)
		arrayLine := strings.Split(data, "|")
		if len(arrayLine) != 3 {

			fmt.Println(time.Now(), data, "数据参数不对")
			continue
		}

		file = arrayLine[0]
		downLoadDir = arrayLine[1]
		WatermarkCon = arrayLine[2]

		fileName, err := s.GetVideoMetadata(file, downLoadDir, WatermarkCon)
		if err != nil {
			//  发送http请求
			fmt.Println(time.Now(), err)
			continue

		}

		// 文件检查完毕 开始运行检测是否有水印

		// 拷贝文件到解码目录
		copyFileToDir(fileName, s.taskOutputDir)

		// 运行解水印程序
		fmt.Println(time.Now(), "开始校验文件是否加水印............................................................................", fileName)

		err = RunScript(s.decodeShell)

		// 读取解水印的结果 确认是否已经增加水印
		content, err := ioutil.ReadFile(s.decodeMarkFile)
		if err != nil {
			// 如果读取文件时出错，使用 log.Fatal 打印错误并退出程序
			log.Fatal(err)
			continue
		}

		// 打印文件内容
		str1 := fmt.Sprintf("%s", content)
		arrayLine = strings.Split(str1, " ")
		k1 := strings.Replace(arrayLine[len(arrayLine)-1], "\n", "", -1)
		fmt.Println(time.Now(), "解码后的水印为..............|"+k1+"|..")
		v1 := s.baseMap[k1]
		if v1 != "" {
			fmt.Println(time.Now(), " 已经有水印......................................", k1, v1, "请求发送数据给对端API")
			//  送http请求给接口机器
			continue

		}

		// 开始加水印

		fmt.Println(time.Now(), "开始加水印....................... ", str1)
		tmptime1 := time.Now().Unix()

		//开始调用SHELL
		err = RunScript(s.encodeShell)
		if err != nil {
			fmt.Println(time.Now(), "错误:", err)
			//  发送http请求

			fmt.Println(time.Now(), err)

			continue

		} else {

			fmt.Println(time.Now(), "完成视频加水印")

		}
		tmptime2 := time.Now().Unix()

		tmpruntime := tmptime2 - tmptime1
		fmt.Println(time.Now(), "当前任务运行时间为...........................", tmpruntime)
		//  拷贝文件到一个特定目录

		nanoString := strconv.FormatInt(time.Now().UnixNano(), 10)
		err = os.MkdirAll(s.upLoadDir+"/"+nanoString, 0755)
		if err != nil {
			// 如果在创建目录时出现错误，则打印错误信息
			fmt.Println("创建目录失败:", err)
			continue
		}

		copyFileToDir(s.taskOutputDir+"/"+s.downLoadFileName, s.upLoadDir+"/"+nanoString)

		//  发送http请求给接口机器

		fmt.Println(time.Now(), "发送HTTP请求给接口机器 文件................................", s.upLoadDir+"/"+nanoString+"/"+s.downLoadFileName)
		var p  PostData 
		p.FileUrl = "http://172.16.44.185/"+nanoString+"/"+s.downLoadFileName 
		fmt.Println("MMMMMMMMMMMM",p.FileUrl )
		p.Hostname =GetHostname()
		sendPostRequest("http://172.16.1.203/submit", p) 


	}
}

//  API  接受参数

type RequestData struct {
	ID           int    `json:"id"`
	File         string `json:"file"`
	Token        string `json:"token"`
	WatermarkCon string `json:"watermark_con"`
	Format       string `json:"format"`
}

func (s *Storage) DownloadMP4File(url, destinationDir string) error {
	// 发起HTTP GET请求以下载文件
	response, err := http.Get(url)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	// 获取文件名
	tokens := strings.Split(url, "/")
	filename := tokens[len(tokens)-1]
	s.downLoadFileName = filename

	// 创建目标文件
	destinationPath := filepath.Join(destinationDir, filename)
	file, err := os.Create(destinationPath)
	if err != nil {
		return err
	}
	defer file.Close()

	// 将HTTP响应的主体复制到目标文件
	_, err = io.Copy(file, response.Body)
	if err != nil {
		return err
	}

	//记录文件名
	return nil
}

// 定义要发送的数据结构
type PostData struct {
	ID       int    `json:"id"`
	FileUrl  string `json:"fileurl"`
	Hostname string `json:"token"`
}

// 发送 POST 请求的函数
func sendPostRequest(url string, data PostData) {
	// 将数据编码为 JSON
	jsonData, err := json.Marshal(data)
	if err != nil {
	         fmt.Println(time.Now(),err)
		 return
	}

	// 创建请求
	request, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
	if err != nil {
		fmt.Println(time.Now(),"汇报数据出错 ",err)
		 return 
	}
	request.Header.Set("Content-Type", "application/json")

	// 发送请求
	client := &http.Client{}
	response,err := client.Do(request)
	if err != nil {
            fmt.Println(time.Now(),err)
	    return
	}
	defer response.Body.Close()
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		fmt.Println("读取响应体失败:", err)
		return
	}

	// 将响应体转换为字符串
	bodyString := string(body)

	// 打印响应体
	fmt.Println("响应体:", bodyString)

}

// createFileAndWriteText 创建文件并写入文本

func createFileAndWriteText(filePath, text string) error {
	// 创建文件
	file, err := os.Create(filePath)
	if err != nil {
		return err
	}
	defer file.Close()

	// 写入文本
	_, err = file.WriteString(text)
	if err != nil {
		return err
	}

	return nil
}

// GetHostname 函数返回当前主机的名称
func GetHostname() (string ) {
	hostname, err := os.Hostname() // 调用os包的Hostname函数获取主机名
	if err != nil {
		return "" // 如果发生错误，返回错误
	}
	return hostname // 成功时返回主机名和nil作为错误
}

func (s *Storage) GetVideoMetadata(videoURL, directory, watermarkcon string) (string, error) {
	// 清理临时文件
	s.RemoveContents(s.basePicDir)
	s.RemoveContents(s.taskInputDir)
	s.RemoveContents(s.taskOutputDir)
	s.RemoveContents(s.decodePicDir)
	s.RemoveContents(s.encodePicDir)

	// 下载文件到指定目录

	// Get the response from the URL
	fmt.Println(time.Now(), "开始下载文件", videoURL, directory)
	err := s.DownloadMP4File(videoURL, directory)

	if err != nil {

		fmt.Println(time.Now(), err)
		return "", err
	}

	tokens := strings.Split(videoURL, "/")
	filename := tokens[len(tokens)-1]

	// 创建目标文件
	destinationPath := filepath.Join(directory, filename)
	fmt.Println(time.Now(), "文件下载完毕", destinationPath)

	fullPath := destinationPath

	// 设置超时，以防 ffprobe 命令花费太长时间
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	// 获取文件的元数据
	metadata, err := ffprobe.GetProbeDataContext(ctx, fullPath)
	if err != nil {
		return "", err
	}

	//检查文件的分辨率 编码格式 时间长度

	for _, stream := range metadata.Streams {
		// 检查是否为视频流
		if stream.CodecType == "video" {
			s.fileWidth = stream.Width
			s.fileHeight = stream.Height
			s.fileDuration = stream.Duration
			s.fileCodecName = stream.CodecName
		}

	}

	fmt.Println(time.Now(), "上传文件", fullPath, "相关参数", s.fileWidth, s.fileHeight, s.fileDuration, s.fileCodecName)

	if s.fileWidth != 1920 || s.fileHeight != 1080 {

		return "", errors.New("分辨率不匹配数据文分辨率 " + strconv.Itoa(s.fileWidth) + ":" + strconv.Itoa(s.fileHeight) + " 系统支持1920:1080")
	}

	if s.fileCodecName != "h264" {

		return "", errors.New("文件的编码格式不匹配  " + s.fileCodecName + " 系统支持h264")
	}

	t1, _ := strconv.ParseFloat(s.fileDuration, 64)
	if t1 > 200 {

		return "", errors.New("文件的时间长度不匹配   " + s.fileDuration + "  系统支持时间不超过60秒")
	}

	// 水印的字符串写 .txt文件 拷贝文件到指定目录
	err = createFileAndWriteText(fullPath+".txt", watermarkcon)

	fmt.Println(time.Now(), "需要拷贝文件到相关的目录............", fullPath, watermarkcon, s.taskInputDir, err)
	// 删除临时文件
	s.RemoveContents(s.taskInputDir)
	copyFileToDir(fullPath, s.taskInputDir)
	copyFileToDir(fullPath+".txt", s.taskInputDir)
	return fullPath, nil
}

// API的接口机器
func (s *Storage) ApiServer() {

	r := gin.Default()
	r.StaticFS("/", http.Dir("upload"))

	r.POST("/submit", func(c *gin.Context) {
		var requestData RequestData

		if err := c.ShouldBindJSON(&requestData); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		//  判断队列状态

		s.baseChan <- requestData.File + "|" + s.downLoadDir + "|" + requestData.WatermarkCon

		fmt.Println(time.Now(), "现有的队列长度", len(s.baseChan))
		c.String(200, "已经进入队列")

	})

	r.Run("0.0.0.0:80") // 默认监听并在 0.0.0.0:8080 上启动服务

}

func main() {
	Storage := InitStorage()
	Storage.basePicDir = "pic"
	Storage.encodePicDir = "pic"
	Storage.decodePicDir = ".de_tmp"
	Storage.baseFileDir = "data/input_videos/videos_0421/input_video"
	Storage.baseShell = "run_encoder.sh"
	Storage.decodeShell = "run_decoder.sh"
	Storage.encodeShell = "run_encoder.sh"
	Storage.taskInputDir = "data/input_videos/videos_0421/input_video"
	Storage.taskOutputDir = "data/output_results/results_0421/encoded_videos"
	Storage.downLoadDir = "download"
	Storage.upLoadDir = "upload"
	Storage.taskStatus = "waiting"
	Storage.datafile = "data/output_results/results_0421/mark_index.json"
	Storage.decodeMarkFile = "data/output_results/results_0421/final_decoded_watermark.txt"
	// 清理文件
	Storage.RemoveContents(Storage.taskInputDir)
	Storage.RemoveContents(Storage.taskOutputDir)
	Storage.InitMarkId(Storage.datafile)
	// 启动后台处理程序
	go Storage.RunTask()

	// 开始启动web接口
	Storage.ApiServer()

}
